/*mpirun -np 4 ./frequencyLingo completeDB.gz 4 out4.gz*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <ctype.h>
#include <time.h>

#include <iostream>
#include <fstream>

#include "zlib.h"
#include "libraries.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LINGOLENGTH  4
#define LENGTHARRAY  4096
#define	ARRAYSIZE    60000
#define NUMPROCESS   4
#define MINSIZEDB    1763074800

#define UMBRAL       0.17
#define MOLSTANDARD  25000
#define CHANGEDIGIT  '?'
#define NUMBERELEM   112
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
struct LingoSim {

  char  *smile;
  int    code;
  float  lingoSimValue;

  LingoSim() {
    smile           = NULL;
    code            = 0;
    lingoSimValue   = 0;
  }

  ~LingoSim() {
    if (smile) delete[] smile;
  }

  struct LingoSim operator=(const struct LingoSim &lingoSim) {

    if (this->smile) delete[] this->smile;
    this->smile = new char[strlen(lingoSim.smile) + 1];
    strcpy(this->smile, lingoSim.smile);

    code          = lingoSim.code;
    lingoSimValue = lingoSim.lingoSimValue;

    return *this;

  };

};

#define LINGOSim struct LingoSim

struct LingoFreq {
   
  char  *lingo;
  int    frequency;

  LingoFreq() {
    this->lingo     = NULL;
    this->frequency = 0;
  }

  ~LingoFreq() {
    if (lingo) delete[] lingo;
  }

  struct LingoFreq operator=(const struct LingoFreq &lingoFreq) {

    if (this->lingo) delete[] this->lingo;
    this->lingo = new char[strlen(lingoFreq.lingo) + 1];
    strcpy(this->lingo, lingoFreq.lingo);

    this->frequency = lingoFreq.frequency;

    return *this;

  };

};

#define LINGOFreq struct LingoFreq
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace LingoSpace {

  bool FindLingo(TNode <LINGOFreq>* NodeT, void* ref = NULL) {
    return (!strcmp(NodeT->Info()->lingo, (char*)ref))?true:false;
  }

  bool MajorFreq(TNode <LINGOFreq>* NodeT, void* ref) {
    int FreqA = NodeT->Info()->frequency;
    int FreqB = ((TNode <LINGOFreq>*)ref)->Info()->frequency;
    return (FreqA > FreqB)?true:false;
  }

  bool AlphLingo(TNode <LINGOFreq>* NodeT, void* ref) {
    char *LingoA = NodeT->Info()->lingo;
    char *LingoB = ((TNode <LINGOFreq>*)ref)->Info()->lingo;
    return (strcmp(LingoA, LingoB) > 0)?true:false;
  }

};
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double obtainLingoSim(char *lingoString1, char *lingoString2) {

  int length1 = strlen(lingoString1), length2 = strlen(lingoString2);
  char tmpLingo1[length1], tmpLingo2[length2];
  int  i, j, k, z;

  //Change the Cl bu L and Br by R in the two lingo sequences
  for (i = 0, j = 0, k = 0; i < ((length1 > length2)?length1:length2); i++) {
    if (i < length1) {
      if      ((lingoString1[i] == 'l') || (lingoString1[i] == 'r')) tmpLingo1[j - 1] = (lingoString1[i] == 'l')?'L':'R';
      else if (isdigit(lingoString1[i])) tmpLingo1[j++] = '?';
      else tmpLingo1[j++] = lingoString1[i];
    }
    if (i < length2) {
      if ((lingoString2[i] == 'l') || (lingoString2[i] == 'r')) tmpLingo2[k - 1] = (lingoString2[i] == 'l')?'L':'R';
      else if (isdigit(lingoString2[i])) tmpLingo2[k++] = '?';
      else tmpLingo2[k++] = lingoString2[i];
    }
  }
  tmpLingo1[j] = '\0'; tmpLingo2[k] = '\0';

  j -= (LINGOLENGTH - 1); k -= (LINGOLENGTH - 1);
  int    L = j + k; //Total Lingos in two sequences
  double similarityValue = 0, Nai, Nbi;

  for (i = 0; i < j + k; i++) {

    char   compare[5];
    bool   analize = true;

    Nai = 0, Nbi = 0;

    if (i < j) {

      strncpy(compare, tmpLingo1 + i, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          Nbi++;
        }
      }
    }
    else {

      strncpy(compare, tmpLingo2 + i - j, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          analize = false;
          break;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += (((Nai - Nbi) >= 0)?(1 - ((Nai - Nbi)/(Nai + Nbi))):(1 - ((Nbi - Nai)/(Nai + Nbi))));
    else                 L--;
  }

  return similarityValue/L;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double obtainLingoSimText(char *lingoString1, char *lingoString2) {

  int length1 = strlen(lingoString1), length2 = strlen(lingoString2);
  char tmpLingo1[length1], tmpLingo2[length2];
  int  i, j, k, z;

  //Change the Cl bu L and Br by R in the two lingo sequences
  for (i = 0, j = 0, k = 0; i < ((length1 > length2)?length1:length2); i++) {
    if (i < length1) {
      if (isdigit(lingoString1[i])) tmpLingo1[j++] = '?';
      else tmpLingo1[j++] = lingoString1[i];
    }
    if (i < length2) {
      if (isdigit(lingoString2[i])) tmpLingo2[k++] = '?';
      else tmpLingo2[k++] = lingoString2[i];
    }
  }
  tmpLingo1[j] = '\0'; tmpLingo2[k] = '\0';

  j -= (LINGOLENGTH - 1); k -= (LINGOLENGTH - 1);
  int    L = j + k; //Total Lingos in two sequences
  double similarityValue = 0, Nai, Nbi;

  for (i = 0; i < j + k; i++) {

    char   compare[5];
    bool   analize = true;

    Nai = 0, Nbi = 0;

    if (i < j) {

      strncpy(compare, tmpLingo1 + i, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          Nbi++;
        }
      }
    }
    else {

      strncpy(compare, tmpLingo2 + i - j, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          analize = false;
          break;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += (((Nai - Nbi) >= 0)?(1 - ((Nai - Nbi)/(Nai + Nbi))):(1 - ((Nbi - Nai)/(Nai + Nbi))));
    else                 L--;
  }

  return similarityValue/L;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void trim(char *stream) {

  int i, j;
  for (i = 0, j = 0; i < strlen(stream); i++)
    if ((stream[i] != '\n') && (stream[i] != '\r'))
        stream[j++] = stream[i];
  stream[j] = '\0';

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool isEmpty(char *stream) {

  if (!stream) return true;
  for (int i = 0; i < strlen(stream); i++)
    if ((stream[i] != '\n') && (stream[i] != '\r') && (stream[i] != ' ') && (stream[i] != '\t') && (stream[i] != '\0')) return false;
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char *extractElement(char *lines, int position) {

  if (position < 1) return NULL;
  char *pch = strtok (lines,", \n");
  if (position == 1) return pch;
  int intPos = 1;
  while (pch != NULL) {
    pch = strtok (NULL, ", \n");
    if (++intPos == position) return pch;
  }
  return NULL;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
long* findVirtualFlags(char *dbName, int process, long *arrFlags) {

  if (!dbName || !process) return NULL;

  long endPos;
  char buffer[LENGTHARRAY];

  gzFile completeDB = gzopen(dbName, "rb9");
  //gzseek(completeDB, MINSIZEDB, SEEK_SET);
  while (!gzeof(completeDB)) gzgets(completeDB, buffer, sizeof(buffer));
  endPos = gztell(completeDB);
  gzclose(completeDB);

  long dif = endPos/process;
  int  processTMP = 1;
  arrFlags =  new long[process];

  if (process == 1) {
    arrFlags[0] = endPos;
    return arrFlags;
  }
  else {

    completeDB = gzopen(dbName, "rb9");
    gzseek(completeDB, 0, SEEK_SET);
    while (!gzeof(completeDB)) {
      gzgets(completeDB, buffer, sizeof(buffer));
      long position = gztell(completeDB);
      if (position >= processTMP*dif) {
        arrFlags[processTMP - 1] = position;
        processTMP++;
        gzseek(completeDB, dif - abs((processTMP*dif) - position), SEEK_CUR);
      }
    }
    arrFlags[processTMP - 1] = endPos;
    gzclose(completeDB);

  }

  return arrFlags;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void MixArrayX(LINGOSim **array1, int n1, LINGOSim **array2, int n2, LINGOSim **array3) {

    int x1 = 0, x2 = 0, x3 = 0;

    while (x1 < n1 && x2 < n2)
      if (array1[x1]->lingoSimValue > array2[x2]->lingoSimValue) {

        if (array3[x3]->smile) delete array3[x3]->smile;
        array3[x3]->smile = new char[strlen(array1[x1]->smile) + 1];
        strcpy(array3[x3]->smile, array1[x1]->smile);
        array3[x3]->code          = array1[x1]->code;
        array3[x3]->lingoSimValue = array1[x1]->lingoSimValue;
        x3++; x1++;

      }
      else {

        if (array3[x3]->smile) delete array3[x3]->smile;
        array3[x3]->smile = new char[strlen(array2[x2]->smile) + 1];
        strcpy(array3[x3]->smile, array2[x2]->smile);
        array3[x3]->code          = array2[x2]->code;
        array3[x3]->lingoSimValue = array2[x2]->lingoSimValue;
        x3++; x2++;

      }
    while (x1 < n1) {

        if (array3[x3]->smile) delete array3[x3]->smile;
        array3[x3]->smile = new char[strlen(array1[x1]->smile) + 1];
        strcpy(array3[x3]->smile, array1[x1]->smile);
        array3[x3]->code          = array1[x1]->code;
        array3[x3]->lingoSimValue = array1[x1]->lingoSimValue;
        x3++; x1++;

    }
    while (x2 < n2) {

        if (array3[x3]->smile) delete array3[x3]->smile;
        array3[x3]->smile = new char[strlen(array2[x2]->smile) + 1];
        strcpy(array3[x3]->smile, array2[x2]->smile);
        array3[x3]->code          = array2[x2]->code;
        array3[x3]->lingoSimValue = array2[x2]->lingoSimValue;
        x3++; x2++;

    }

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void MixArray(LINGOSim **array, int n) {

  LINGOSim **array1, **array2;
  int n1, n2, i, j;

  if (n > 1) {

    if (n%2 == 0)   n1 = n2 = (int)n / 2;
    else         {  n1 = (int)n / 2; n2 = n1 + 1; }

    array1  = new LINGOSim*[n1];
    array2  = new LINGOSim*[n2];

    for (i = 0; i < n1; i++) {

      array1[i]  = new LINGOSim;

      array1[i]->smile         = new char[strlen(array[i]->smile) + 1];
      strcpy(array1[i]->smile, array[i]->smile);
      array1[i]->code          = array[i]->code;
      array1[i]->lingoSimValue = array[i]->lingoSimValue;

    }
    for (j = 0; j < n2; i++, j++) {

      array2[j]  = new LINGOSim;

      array2[j]->smile         = new char[strlen(array[i]->smile) + 1];
      strcpy(array2[j]->smile, array[i]->smile);
      array2[j]->code          = array[i]->code;
      array2[j]->lingoSimValue = array[i]->lingoSimValue;

    }

    MixArray(array1, n1);
    MixArray(array2, n2);
    MixArrayX(array1, n1, array2, n2, array);

    for (int i = 0; i < n1; i++) if (array1[i]) delete array1[i];
    if (array1) delete[] array1;
    for (int i = 0; i < n2; i++) if (array2[i]) delete array2[i];
    if (array2) delete[] array2;

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void MergeSort(LINGOSim **array, int n) {

  MixArray(array, n);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* splitPosLine(char *line, int pos) {

  if (pos < 0) return NULL;

  int   count = 1, length = 0;
  char *tmpResult = new char[strlen(line) + 1];

  for (int i = 0; i < strlen(line); i++) {
    if (line[i] == ',') { count++; i++; }
    if (pos == count) tmpResult[length++] = line[i];
  }
  tmpResult[length] = '\0';

  char *result = new char[length + 1];
  strcpy(result, tmpResult);
  delete[] tmpResult;

  return result;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
LINGOSim* extractMol(char *line) {

  LINGOSim *result = new LINGOSim;
  result->smile          = splitPosLine(line, 1);
  char *received = splitPosLine(line, 2);
  result->code           = atoi(received);
  delete[] received;
  received = splitPosLine(line, 3);
  result->lingoSimValue  = atof(received);
  delete[] received;
   return result;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
LINGOSim **uncompressResult(char **result, int numberPart, int numberMolecules) {

  LINGOSim **listMatch = new LINGOSim*[numberMolecules];
  int       countMolecules = 0;

  for (int i = 0; i < numberPart; i++) {

    char *pch = strtok(result[i], "\n");
    if (pch) listMatch[countMolecules++] = extractMol(pch);

    while (pch != NULL) {
      pch = strtok(NULL, "\n");
      if (!pch) break;
      listMatch[countMolecules++] = extractMol(pch);
    }

  }

  return listMatch;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool prioriAnalysis(int length1, int length2, float umbral) {

  if ((1 - ((abs(length1 - length2))/(length1 + length2 - 6))) >= umbral) return true;
  return false;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool analysisCompResult(char *filename, int numberMol, int &countLine) {

  if (!numberMol || !filename) return false;

  FILE *File = fopen(filename, "r");
  countLine  = 0;

  if (!File) return false;

  char buffer[1024];

  while (!feof(File)) {
    fgets(buffer, 1024, File);
    if (strlen(buffer)) countLine++;
  }
  fclose(File);
  if (countLine - 1 == numberMol) return true;
  return false;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool deleteMolPosition(char **compressResultData, int position, int rank) {

  if (position < 1) return false;

  char *temporalResultData = new char[strlen(*compressResultData) + 1];
  strcpy(temporalResultData, *compressResultData);

  *(compressResultData)[0] = '\0';

  char *pch           = strtok(temporalResultData,"\n");
  int   internalCount = 1;
  char  name[300];

  sprintf(name, "errorMolecules%02d\0", rank);
  FILE *File = fopen(name, "a+");

  while (pch != NULL) {
    if (internalCount++ != position) sprintf(*compressResultData, "%s%s\n\0", *compressResultData, pch);
    else                             fprintf(File, "%s\n", pch);
    pch = strtok(NULL, "\n");
  }

  fclose(File);

  delete[] temporalResultData;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void standardizeMolecules(char **compressResultData, char *xmlConvertion, int rank, int numberIter, int numberMol) {

  printf("Node %d create Temporal File (Iter %d)\n", rank, numberIter);

  bool  condition     = true;
  char *nameCompStand = new char[400];

  while (condition) {

    int countLine = 0;

    sprintf(nameCompStand, "standardize%02d.smile\0", rank);
    FILE *computeStandardizeFile = fopen(nameCompStand, "w");
    fwrite(*compressResultData, 1, strlen(*compressResultData), computeStandardizeFile);
    fclose(computeStandardizeFile);

    printf("Node %d execute (Iter %d) -> ./standardize -c %s standardize%02d.smile > out%02d.smileOut\n", rank, numberIter, xmlConvertion, rank, rank);

    sprintf(nameCompStand, "./standardize -c %s standardize%02d.smile > out%02d.smileOut\0", xmlConvertion, rank, rank);
    try { system(nameCompStand); }
    catch (std::exception&) {}

    printf("Node %d open Result File (Iter %d)\n", rank, numberIter);
    sprintf(nameCompStand, "out%02d.smileOut\0", rank);

    if (analysisCompResult(nameCompStand, numberMol, countLine)) break;
    else {

      printf("Node %d Delete Molecule %d (Iter %d)\n", rank, countLine, numberIter);

      deleteMolPosition(compressResultData, countLine, rank);

      sprintf(nameCompStand, "rm standardize%02d.smile out%02d.smileOut\0", rank, rank);
      system(nameCompStand);

      numberMol--;

    }

  }

  long   lSize;

  sprintf(nameCompStand, "out%02d.smileOut\0", rank);
  FILE *computeStandardizeFile = fopen(nameCompStand, "r");

  fseek(computeStandardizeFile , 0 , SEEK_END);
  lSize = ftell(computeStandardizeFile);
  rewind(computeStandardizeFile);

  delete[] *compressResultData;
  *compressResultData = new char[lSize + 1];
  fread(*compressResultData, 1, lSize, computeStandardizeFile);
  (*compressResultData)[lSize] = '\0';

  fclose(computeStandardizeFile);

  printf("Node %d execute (Iter %d) -> rm standardize%02d.smile out%02d.smileOut\n", rank, numberIter, rank, rank);

  sprintf(nameCompStand, "rm standardize%02d.smile out%02d.smileOut\0", rank, rank);
  system(nameCompStand);

  delete[] nameCompStand;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool checkMolErr(char *molecule, char *erroMolecules) {

  char *temporalMolecule = new char[strlen(erroMolecules) + 1];
  strcpy(temporalMolecule, erroMolecules);
  char *pch              = strtok(temporalMolecule,"\n");

  while (pch != NULL) {
    trim(pch); trim(molecule);
    if (!strcmp(molecule, pch)) {
      delete[] temporalMolecule;
      return true;
    }
    pch = strtok(NULL, "\n");
  }
  delete[] temporalMolecule;
  return false;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void storedDiffCharacter(char *string, int **pos){

  if (!string || !pos) return;
  for (int i = 0; i < strlen(string); i++) if (!iscntrl(string[i])) (*pos)[((int)string[i])] = 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void updateLingoInFile(TListE <LINGOFreq>* listLingos, char *lingo) {

  LINGOFreq *tmpLingo = new LINGOFreq;
  
  tmpLingo->lingo = new char[strlen(lingo) + 1];
  strcpy(tmpLingo->lingo, lingo);
  tmpLingo->frequency = 1;
  
  LINGOFreq *findLingo = listLingos->FirstOcurrence(LingoSpace::FindLingo, lingo);

  if (!findLingo)  listLingos->AddIni(tmpLingo);
  else { 
    findLingo->frequency++;
    delete tmpLingo;
  }
  
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void FindLingos(TListE <LINGOFreq>* listLingos, char *element1, int length) {

  if (!element1) return;

  char lingoTmp[length + 1];

  for (int i = 0; i < strlen(element1) - length + 1; i++) {
    strncpy(lingoTmp, (element1 + i), length);
    lingoTmp[length] = '\0';
    updateLingoInFile(listLingos, lingoTmp);
  }
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void changeBondCharacter(char **molecule) {

  if (!molecule || !*molecule ) return;
  for (int i = 0; i < strlen(*molecule); i++)
    if (isdigit((*molecule)[i]))
      (*molecule)[i] = CHANGEDIGIT;
    
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void completeListLingo(TListE <LINGOFreq>* listLingos, char* compressResultData) {
    
  char *pch = strtok(compressResultData,"\n");
  
  while (pch != NULL) {
      
    char *lingo     = splitPosLine(pch, 1);
    char *frequency = splitPosLine(pch, 2);

    LINGOFreq *findLingo = listLingos->FirstOcurrence(LingoSpace::FindLingo, lingo);

    if (findLingo)
      findLingo->frequency += atoi(frequency);
    else {
      LINGOFreq *tmpLingo = new LINGOFreq;
      tmpLingo->lingo = new char[strlen(lingo) + 1];
      strcpy(tmpLingo->lingo, lingo);
      tmpLingo->frequency = atoi(frequency);
      
      listLingos->AddIni(tmpLingo);
    }

    delete[] lingo;
    delete[] frequency;

    pch = strtok (NULL, "\n");
  }
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int main(int argc, char *argv[]) {

  time_t        start,end;
  char         *dbName  = NULL, *compressResultData = NULL;
  char         *outName = NULL;

  int           lengthLingo = 0;

  TListE <LINGOFreq>* listLingos = new TListE <LINGOFreq> ();

  time (&start);

  //Get the input parameters 1. Database 2. The SMILES string
  if (argc < 4) {
    cout << "Error in the input arguments. The execution must be: " << endl;
    cout << "./frequencyLingo <input file> <lingo size> <output file>" << endl;
    return 0;
  }
  else {
    dbName      = new char[strlen(argv[1]) + 1];
    strcpy(dbName, argv[1]);
    lengthLingo = atoi(argv[2]);
    outName     = new char[strlen(argv[3]) + 1];
    strcpy(outName, argv[3]);
  }

  long *flags;

  char buffer[LENGTHARRAY], bufferTmp[LENGTHARRAY];

  gzFile completeDB = gzopen(dbName,  "rb9");

  while (!gzeof(completeDB)) {

    gzgets(completeDB, buffer, sizeof(buffer));
    if (!buffer || isEmpty(buffer)) continue;
    strcpy(bufferTmp, buffer);

    long  endPosTmp = gztell(completeDB);
    char *element1  = new char[strlen(buffer) + 1];
    strcpy(element1, extractElement(bufferTmp, 1));
    
    printf("Molecule: %s\n", element1);
    trim(element1); changeBondCharacter(&element1);

    if (strlen(element1) >= lengthLingo)
      FindLingos(listLingos, element1, lengthLingo);

    delete[] element1;

    if (endPosTmp >= finalR) break;

  }

  if (err == MPI_SUCCESS) MPI_Barrier(MPI_COMM_WORLD);

  printf("Preparing to Join the Result Processed in the Node %d...\n", rank);

  //Group the result in the master Node
  if (rank) {

    listLingos->QuickSortList(LingoSpace::MajorFreq, NULL);
    gzFile tmpDB = gzopen(tmpName, "wb9");

    for (int i = 0; i < listLingos->Length(); i++) {
      LINGOFreq *tmpLingoPro = (LINGOFreq *)listLingos->Get(i);
      char *buffer = new char[strlen(tmpLingoPro->lingo) + 100];
      sprintf(buffer, "%s,%d\n\0", tmpLingoPro->lingo, tmpLingoPro->frequency);
      gzwrite(tmpDB, buffer, strlen(buffer));
      delete[] buffer;
    }

    long  sizeCompressResultData = gztell(tmpDB);
    gzclose(tmpDB);

    if (listLingos) delete listLingos;
    
    compressResultData  = new char[sizeCompressResultData + 1];

    tmpDB = gzopen(tmpName, "rb9");
    gzread(tmpDB, compressResultData, sizeCompressResultData);
    gzclose(tmpDB);

    printf("Sending to root Node the Result obtained in Node %d...\n", rank);

    MPI_Send(&sizeCompressResultData,   1, MPI_LONG, 0, (30 + rank), MPI_COMM_WORLD);
    MPI_Send(tmpName                , 300, MPI_CHAR, 0, (40 + rank), MPI_COMM_WORLD);

    if (compressResultData) delete[] compressResultData;

  }
  else {

    for (int i = 1; i < sizeRank; i++) {

      long  sizeCompressResultData = 0;
      MPI_Recv(&sizeCompressResultData,   1, MPI_LONG, i, (30 + i), MPI_COMM_WORLD, &Stat);
      MPI_Recv(tmpName                , 300, MPI_CHAR, i, (40 + i), MPI_COMM_WORLD, &Stat);
      
      compressResultData = new char[sizeCompressResultData + 1];

      gzFile tmpDB = gzopen(tmpName, "rb9");
      gzread(tmpDB, compressResultData, sizeCompressResultData);
      gzclose(tmpDB);

      char nameCompStand[300];
      sprintf(nameCompStand, "rm %s\0", tmpName);
      system(nameCompStand);
    
      printf("Receving the result from Node %d...\n", i);

      completeListLingo(listLingos, compressResultData);
      delete[] compressResultData;

    }

    listLingos->QuickSortList(LingoSpace::MajorFreq, NULL);

    gzFile tmpDB = gzopen(outName, "wb9");
      for (int i = 0; i < listLingos->Length(); i++)
        gzprintf(tmpDB, "%s,%d\n\0", ((LINGOFreq*)listLingos->Get(i))->lingo, ((LINGOFreq*)listLingos->Get(i))->frequency);
    gzclose(tmpDB);
    
    if (listLingos) delete listLingos;
    
    printf("Finish...\n");

  }

  //if (err == MPI_SUCCESS) MPI_Barrier(MPI_COMM_WORLD);

  if (dbName)     delete[] dbName;
  if (flags)      delete[] flags;

  time (&end);
  if (!rank) {
      int timeSeconds = (int)difftime(end,start);
      int timeMinutes = (int)(timeSeconds/60);
      int timeHours   = (int)(timeMinutes/60);
      timeSeconds    %= 60;
      timeMinutes    %= 60;
      printf("Elapsed Time: %02d:%02d:%02d (HH:MM:SS)\n", timeHours, timeMinutes, timeSeconds);
  }

  if (err == MPI_SUCCESS) MPI_Finalize();

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
